package com.yhq.spring.spring;

import com.yhq.spring.spring.conf.*;
import com.yhq.spring.spring.po.Student;
import com.yhq.spring.spring.po.User;
import com.yhq.spring.spring.service.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author hqyin
 * @date 2023/1/30 10:47 上午
 */
@Slf4j
public class AnnotationTest {
    public static void main(String[] args) {
        //test AnnotationConfigApplicationContext
        test_AnnotationConfigApplicationContext();



        // test AnnotationConfigApplicationContext scan方法
        //test_AnnotationConfigApplicationContext_scan();

        //测试属性
        //test_attr();

        //测试父子BeanDefinition
        //test_parentChildBd();

        //测试 Resource
        //test_resource();

        //test BeanDefinition实现类
        //test_ScannedGenericBeanDefinition();
        //test_AnnotatedGenericBeanDefinition();
        //test_ConfigurationClassBeanDefinition();


        //test AnnotatedBeanDefinitionReader
        //test_AnnotatedBeanDefinitionReader();

        //test ClassPathBeanDefinitionScanner
        //test_ClassPathBeanDefinitionScanner();

        //test BeanFactory
        //test_BeanFactory();

        // test FactoryBean
        //test_FactoryBean();

        //TestInitializingMethod
        //test_InitializingMethod();

        //test BeanFactoryPostProcessor
        //test_BeanFactoryPostProcessor();

        //test MetaDataReader
        //test_MetaDataReader();

        // test 类型转换
        //test_ConversionService();
        //test_TypeConverter();
        //test_PropertyEditor();

        //test 运行时环境
        //test_Environment();

        //测试解析占位符
        //test_placeholder();

    }

    private static double calculateByTopStep(double usage, Map<Double, Double> steps) {
        //steps顺序排序
        List<Double> stepsKey = steps.keySet().stream().sorted(Comparator.naturalOrder()).filter(e->e>0.0).collect(Collectors.toList());
        Double highestPrice = steps.get(0.0);
        for (Double stepKey : stepsKey) {
            if(usage <= stepKey) {
                return steps.get(stepKey) * usage;
            }
        }
        return usage * highestPrice;
    }

    private static double calculateByHierarchicalStep(double usage, Map<Double, Double> steps) {
        List<Double> stepsKey = steps.keySet().stream().sorted(Comparator.naturalOrder()).filter(e->e>0.0).collect(Collectors.toList());
        Double highestPrice = steps.get(0.0);
        double sum = 0;
        double oldStep = 0;
        for (int i = 0; i <= stepsKey.size(); i++) {
            if(i == stepsKey.size()) {
                sum += (usage-oldStep)*highestPrice;
                break;
            }
            double curStep = stepsKey.get(i);
            if(usage > curStep) {
                sum +=  (curStep-oldStep)*steps.get(curStep);

            } else {
                sum += (usage-oldStep)*steps.get(curStep);
                break;
            }
            oldStep = curStep;
        }
        return sum;
    }


    private static String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;

    private static String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;

    private static void test_placeholder() {
        String data = "[工单] ${DeptName} | ${JobName}";

        int startIndex = data.indexOf(SystemPropertyUtils.PLACEHOLDER_PREFIX);
        if (startIndex == -1) {
            return;
        }

        StringBuilder result = new StringBuilder(data);
        while (startIndex != -1) {
            int endIndex = findPlaceholderEndIndex(result,startIndex);
            String key = data.substring(startIndex+SystemPropertyUtils.PLACEHOLDER_PREFIX.length(),endIndex);
            log.info("key = {}",key);
            //result.append(data.substring(0,))
            String value = "哈哈"+startIndex;
            result.replace(startIndex, endIndex + placeholderSuffix.length(), value);
            startIndex = result.indexOf(placeholderPrefix, startIndex + value.length());;
        }

    }

    private static int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
        int index = startIndex + placeholderPrefix.length();
        while (index < buf.length()) {
            if (StringUtils.substringMatch(buf, index, placeholderSuffix)) {
                return index;
            } else {
                index++;
            }
        }
        return -1;
    }

    private static void test_AnnotationConfigApplicationContext_scan() {
        //FactoryBean,BeanFactory
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.refresh();
        context.scan("com.yhq.spring.annotation.service");
        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }

    private static void test_Environment() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Map<String, Object> sysEnv = context.getEnvironment().getSystemEnvironment();

        System.out.println("============打印运行时环境数据==============");
        System.out.println(sysEnv);

        System.out.println("=============打印运行时系统变量=============");
        Map<String, Object> sysProperties = context.getEnvironment().getSystemProperties();
        System.out.println(sysProperties);

        System.out.println("============答应运行时环境数据地址==============");
        MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
        System.out.println(propertySources);

        System.out.println("==============打印系统变量============");
        System.out.println(context.getEnvironment().getProperty("NO_PROXY"));
        System.out.println(context.getEnvironment().getProperty("sun.jnu.encoding"));

        System.out.println("==============打印自定义properties文件变量============");
        System.out.println(context.getEnvironment().getProperty("test.yhq"));

    }

    private static void test_PropertyEditor() {
        StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
        propertyEditor.setAsText("小明");
        User value = (User) propertyEditor.getValue();
        System.out.println(value);
    }

    private static void test_TypeConverter() {
        SimpleTypeConverter typeConverter = new SimpleTypeConverter();
        typeConverter.registerCustomEditor(User.class,new StringToUserPropertyEditor());
        User value = typeConverter.convertIfNecessary("李四",User.class);
        System.out.println(value);
    }

    private static void test_ConversionService() {
        DefaultConversionService conversionService = new DefaultConversionService();
        conversionService.addConverter(new StringToUserConverter());
        User value = conversionService.convert("小张",User.class);
        System.out.println(value);
    }

    @SneakyThrows
    private static void test_MetaDataReader() {
        SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
        MetadataReader metadataReader = metadataReaderFactory.getMetadataReader("com.yhq.spring.annotation.po.User");

        //获取类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        System.out.println(classMetadata.getClassName());

        //获取类上注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        for (String annotationType : annotationMetadata.getAnnotationTypes()) {
            System.out.println(annotationType);
        }
    }

    private static void test_BeanFactoryPostProcessor() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        FruitService fruitService = (FruitService) context.getBean("fruitService");
        fruitService.show();
    }

    private static void test_InitializingMethod() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        TestInitializingMethod testInitializingMethod = (TestInitializingMethod) context.getBean("testInitializingMethod");
        UserService userService = (UserService) context.getBean("userService");
        System.out.println(userService);
        userService.test();
    }

    private static void test_AnnotationConfigApplicationContext() {
        //FactoryBean,BeanFactory
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }

    private static void test_FactoryBean() {
        //FactoryBean,BeanFactory
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        //使用FactoryBean创建对象
        Student stu = (Student) context.getBean("student");
        System.out.println(stu);

        //获取FactoryBean工厂对象
        StudentFactoryBean factoryBean = (StudentFactoryBean) context.getBean("&student");
        System.out.println(factoryBean);
    }


    private static void test_BeanFactory() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        System.out.println(userService);

    }

    private static void test_ClassPathBeanDefinitionScanner() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory);
        scanner.scan("com.yhq.spring.annotation.service");
        System.out.println(factory.getBean("userService"));
    }

    private static void test_AnnotatedBeanDefinitionReader() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory);
        reader.register(User.class);
        User user = (User) factory.getBean("user");
        System.out.println(user);
    }

    private static void test_ScannedGenericBeanDefinition() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(context.getBeanDefinition("userService").getClass());
    }

    private static void test_AnnotatedGenericBeanDefinition() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(context.getBeanDefinition("appConfig").getClass());
    }

    private static void test_ConfigurationClassBeanDefinition() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(context.getBeanDefinition("personService").getClass());
    }

    @SneakyThrows
    private static void test_resource() {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        //获取某个具体类BeanDefinition的source
        BeanDefinition bd = context.getBeanDefinition("userService");
        System.out.println(bd.getSource());

        //根据全路径获取类的source，协议是file
        Resource resource = context.getResource("file:/Users/hqyin/Documents/MyProjects/spring-test/src/main/java/com/yhq/spring/annotation/po/User.java");
        System.out.println(resource.getFilename());
        System.out.println(resource.getURI());

        //获取网络资源
        Resource networkResource = context.getResource("https://www.baidu.com");
        System.out.println(networkResource.getURI());

        //获取classpath下的文件，协议：classpath
        Resource classpathResource = context.getResource("classpath:com/yhq/spring/annotation/po/Student.class");
        System.out.println(classpathResource.getFilename());
        System.out.println(classpathResource.getURI());

        //一次行获取多个资源
        Resource[] multiResource = context.getResources("classpath:com/yhq/spring/annotation/po/*.class");
        for (Resource rsc : multiResource) {
            System.out.println(resource.getFilename());
        }

    }

    /**
     *
     */
    private static void test_parentChildBd() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        GenericBeanDefinition parentBd = new GenericBeanDefinition();
        parentBd.setScope(BeanDefinition.SCOPE_SINGLETON);
        parentBd.setBeanClass(ParentService.class);
        parentBd.setAbstract(true);
        parentBd.setAttribute("xxx", "codebear");
        parentBd.getConstructorArgumentValues().addGenericArgumentValue("001");
        parentBd.getConstructorArgumentValues().addGenericArgumentValue("Lisa");
        context.registerBeanDefinition("parentService", parentBd);

        GenericBeanDefinition childBd = new GenericBeanDefinition();
        childBd.setParentName("parentService");
        childBd.setBeanClass(ChildService.class);
        context.registerBeanDefinition("childService", childBd);

        context.refresh();


        BeanDefinition child = context.getBeanFactory().getMergedBeanDefinition("childService");
        for (String s : child.attributeNames()) {
            System.out.println(s + ":" + child.getAttribute(s));
        }
        System.out.println("scope:" + child.getScope());

        System.out.println("-------------------");

        ChildService service = context.getBean(ChildService.class);
        System.out.println(service.getName());
        System.out.println(service.getId());





    }

    /**
     * 测试BeanDefinition扩展字段
     * BeanDefinition中的attribute作用：当BeanDefinition中的基本属性不够用的时候，再在attribute增加扩展属性
     * AttributeAccessorSupport定义了一个Map存储扩展属性：private final Map<String, Object> attributes = new LinkedHashMap<>();
     */
    private static void test_attr() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        BeanDefinition bd =  context.getBeanDefinition("appConfig");
        for (String attributeName : bd.attributeNames()) {
            System.out.println(attributeName+":"+bd.getAttribute(attributeName));
        }
    }
}
